#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iostream>
#include <string>
#include <cstring>
#include <sstream>
#include <list>
#include <pthread.h>

#include "rpc.hpp"
#include "message.hpp"
using namespace std;

#define QUEUESIZE 5

char serverip[256];
int serverport;

int forclient;
int forbinder;

int clientbinder;

struct myfunction {
	char name[20];
	int argnum;
	int *argtype;
	skeleton func;
};

list<myfunction*> functionlist;

int createsocket(in_addr_t ip, int port){	
	 /*create the socket*/
    int sid = socket(AF_INET,SOCK_STREAM,0);
    if(sid == -1) {
        cerr << "fail to create socket" << endl;
        return -1;
    }

	struct sockaddr_in server;

    /*set up the socket*/
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = (in_addr_t)ip;
    server.sin_port = port;

	 if(connect(sid,(struct sockaddr *)&server,sizeof(server))== -1) {
        cerr << "fail to connect to the server" << endl;
        exit(-1);
    }
	return sid;
}
int connecttobinder(){
	char * binderip = getenv("SERVER_ADDRESS");
    char * binderport = getenv("SERVER_PORT");	
	struct hostent *host = gethostbyname(binderip);
    struct in_addr **hostip = (struct in_addr**)host->h_addr_list;
	in_addr_t myip = inet_addr(inet_ntoa(**hostip));
	
	int sid = createsocket(myip, htons(atoi(binderport)));
	if(sid < 0) {
		cerr << "fail to connect to the server" << endl;
        exit(-1);
	}
	return sid;
}


int establish()
{
    int socket_fd;
    struct sockaddr_in server;
    
    /*clear out address*/
    memset(&server, 0, sizeof(sockaddr_in));
    
    /*set up the host addr type, port number and ip address of server*/
    server.sin_family = AF_INET;
    server.sin_port = 0;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    
    /*create a socket*/
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if( socket_fd < 0 ){
		cerr << "fail to create socket" << endl;
    	exit(-1);
	}
    
    /*rebind multi-connections*/
    int yes = 1;
    if((setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) < 0 ){
        cerr << "fail to set socket option" << endl;
        exit(-1);
    }
    
    /*bind the socket*/
    if( bind(socket_fd, (struct sockaddr*)&server, sizeof(struct sockaddr)) < 0 ){
        close(socket_fd);
		cerr << "fail to bind the socket" << endl;
        exit(-1);
    }
    
    /*listen on the socket*/
    if( listen(socket_fd, QUEUESIZE) < 0 ){
		cerr << "fail to listen on the socket" << endl;
        exit(-1);
    }
    
    /*print server address*/
    gethostname(serverip, 256);
    /*print port number*/
    socklen_t server_size = sizeof(server);
    getsockname(socket_fd, (struct sockaddr *)&server, &server_size);
    
    //strcpy(serverip, hostname);
	serverport = ntohs(server.sin_port);
	cout << "serverip " << serverip << " serverport " << serverport << endl; 
	return socket_fd;
}

void *provideservice(void *threadid) {
	/*set up socket*/
	cout << "provideservice" << endl;
    struct sockaddr_in client;
    socklen_t client_size;
	int result;
    int new_fd;
	fd_set readset;
    int fds[QUEUESIZE] = {0};
    /*accept connection from client, and receive or send msg to client*/
	for(;;) {
		cout << "in for loop" << endl;
        /*select the client*/
        FD_ZERO(&readset);
        FD_SET(forclient, &readset);
		for(int i = 0; i < QUEUESIZE; i++){
            if(fds[i] > 0) FD_SET(fds[i], &readset);
        }
        result = select(FD_SETSIZE, &readset, NULL, NULL, NULL);
		cout << "after select client" << endl;
        if(result < 0){
            cerr << "fail to select readable sockets" << endl;
            exit(-1);
        }
        if(result == 0){
            cerr << "cilent has exited" << endl;
        }
        /*accept the connection from client*/
		cout << "accept the connection" << endl;
        if(FD_ISSET(forclient, &readset) ){
            client_size = sizeof(client);
            new_fd = accept(forclient,(struct sockaddr*)&client,&client_size);
            if( new_fd < 0 ){
                cerr << "cilent closed the socket" << endl;
                exit(-1);
            }
            
            if( new_fd == 0 ){
                cerr << "cilent closed the socket" << endl;
                exit(-1);
            }
            
            int j;
            for(j = 0; j < QUEUESIZE; j++){
                if(fds[j] == 0){
                    fds[j] = new_fd;
                    break;
                }
            }
            if( j == QUEUESIZE )
                close(new_fd);
		}
		cout << "provide service miao" << endl;
        /*provide service to client */
        for(int i=0; i<QUEUESIZE; i++){
            if(FD_ISSET(fds[i], &readset)){
				cout << "wait for client" << endl;
               int msg_length;
				int size = recv(fds[i],&msg_length,sizeof(int),MSG_WAITALL);
                if( size != sizeof(int)){
					close(fds[i]);
					fds[i] = 0;
					FD_CLR(fds[i], &readset); // remove fd
					cerr << "fail to receive msg length" << endl;
					continue;
				}
				cout << "msg size: " << msg_length << endl;
				
				/*receove type of msg*/
				int msg_type;
				size = recv(fds[i], &msg_type, sizeof(int),MSG_WAITALL);
				if( size != sizeof(int)){
					close(fds[i]);
					fds[i] = 0;
					FD_CLR(fds[i], &readset); // remove fd
					cerr << "fail to receive type" << endl;
					continue;
				}
				cout << "msg type: " << msg_type << endl;
				
				/*receive the msg*/
				int read_offset = 0;
				char buffer[msg_length];
				memset(buffer,0,sizeof(buffer)); // clean buffer
				bool success = true;
				while(read_offset < msg_length){
					size = recv(fds[i], buffer, msg_length, MSG_WAITALL);
					if(size < 0){
						close(fds[i]);
						fds[i] = 0;
						FD_CLR(fds[i], &readset);
						success = false;
						break;
					}
					read_offset += size;
				}
				if(!success) continue;
				cout << "got msg: " << endl;

				
				char name[20];
				int argnum = 0;
				memcpy( &argnum, &buffer[20], sizeof(int));
				int argTypes[argnum];
				void** args = new void*[argnum-1];
				myUnmarshall(msg_type, buffer, msg_length, name, argTypes, args, NULL, NULL, 0);
				//execute
				skeleton func;
				list<myfunction*>::iterator f;
				bool hasfunction = true;
				for(f = functionlist.begin(); f != functionlist.end(); f++){
						if(strcmp(name,(*f)->name) == 0 && (*f)->argnum == argnum) {
							int *tempargtype = (*f)->argtype;	
							for(int j=0; j< argnum; j++){
								if(argTypes[j] != tempargtype[j]){ 
									hasfunction = false;
									break;
								}
							}
							if(hasfunction) {
								func = (*f)->func;
								break;
							}
						}
				}

				//if success
				if(hasfunction) {
					func(argTypes,args);
					cout << "in success" << endl;
					msg_type = EXECUTE_SUCCESS;
					char *exesuccess = myMarshall(msg_type, &msg_length, name, argTypes, args, NULL, NULL, 0);
					cout << "after marshall" << endl;
					send(fds[i],&msg_length,sizeof(int),0);
					send(fds[i],&msg_type,sizeof(int),0);
					send(fds[i], exesuccess, msg_length, 0);
					
				}
				else {
					cout << "in failure" << endl;
					msg_type = EXECUTE_FAILURE;
					char *exefail = myMarshall(msg_type, &msg_length, NULL, NULL, NULL, NULL, NULL, -1);
					send(fds[i],&msg_length,sizeof(int),0);
					send(fds[i],&msg_type,sizeof(int),0);
					send(fds[i], exefail, msg_length, 0);
				}
				cout << "finish" << endl;
				close(fds[i]);
				fds[i] = 0;
				FD_CLR(fds[i], &readset);
			}
        }
	}
	
    /*close the socket (never get to here)*/
	close(forclient);
	cout << "finish service" << endl;
    pthread_exit(NULL);
}

void *keepconnection(void *threadid) {
	cout << "keep connection" << endl;
	int recv_size = 4;
	char recv_msg[recv_size];
	cout << "before receive from binder" << endl;
	memset(recv_msg,0,sizeof(recv_msg));
	recv(forbinder, recv_msg, recv_size, MSG_WAITALL);
	cout << "after receive" << endl;
	memset(recv_msg,0,sizeof(recv_msg));
	recv(forbinder, recv_msg, recv_size, MSG_WAITALL);

	close(forbinder);
	cout << "finish keep" << endl;
    pthread_exit(NULL);
}

//int (*skeleton)(int *, void **);

int rpcInit() {
	forbinder = connecttobinder();
	forclient = establish();
	return 0;
}

int rpcCall(char* name, int* argTypes, void** args) {
	
	clientbinder = connecttobinder();
	int type = LOC_REQUEST;
	int buffersize;
	//LOC_REQUEST, name, argTypes
	char *buffer = myMarshall(type, &buffersize,name,argTypes,NULL,NULL,NULL,0);
	char myname[20];
	int myargTypes[(buffersize-20)/sizeof(int)];	
	myUnmarshall(type, buffer,buffersize, myname, myargTypes, NULL, NULL, NULL, 0);
	cout << "buffersize: " << buffersize << endl;
	cout << "name: " << myname << endl;
	cout << "typesize: " << buffersize-20 << endl;
	
	send(clientbinder, &buffersize, sizeof(int),0);
	send(clientbinder, &type, sizeof(int),0);
	send(clientbinder, buffer, buffersize,0);
	
	// receive back from binder
	int recv_size, recv_type;
	recv(clientbinder,&recv_size,sizeof(int),MSG_WAITALL);
	recv(clientbinder,&recv_type,sizeof(int),MSG_WAITALL);
	int recv_offset = 0;
	char recv_msg[recv_size];
	memset(recv_msg,0,sizeof(recv_msg)); // clean buffer
	while(recv_offset < recv_size){
		int size = recv(clientbinder, recv_msg, recv_size, MSG_WAITALL);
		recv_offset += size;
	}
	in_addr_t recv_ip;
	int recv_port;
	int reasoncode;
	myUnmarshall(recv_type, recv_msg,recv_size, NULL, NULL, NULL, &recv_ip, &recv_port, &reasoncode);
	if(recv_type == LOC_SUCCESS){
		cout << "got ip: " << recv_ip << endl;
		cout << "got port:" << recv_port << endl;
	}
	else if(recv_type == LOC_FAILURE){
		cout << "reasoncode in binder: " << reasoncode << endl;
		//return 0;
	}
	
	//EXECUTE, name, argTypes, args
	int msg_size = 0;
	type = EXECUTE;
	cout << "start execute" << endl;
	char *msg = myMarshall(EXECUTE, &msg_size,name, argTypes, args, NULL, NULL, 0);
	cout << "finish marshall" << endl;
	cout << recv_ip << "~~" << recv_port << endl;
	int sid = createsocket(recv_ip, htons(recv_port));
	cout << "msg_size: " << msg_size << endl;
	send(sid, &msg_size, sizeof(int),0);
	send(sid, &type, sizeof(int),0);
	send(sid, msg, msg_size,0);

    cout << "finish sending" << endl;
	// receive back from server
	int recv_size2, recv_type2;
	recv(sid,&recv_size2,sizeof(int),MSG_WAITALL);
	recv(sid,&recv_type2,sizeof(int),MSG_WAITALL);
    cout << "finish receiving " << endl;
	recv_offset = 0;
	char recv_msg2[recv_size2];
	memset(recv_msg2,0,sizeof(recv_msg2)); // clean buffer
	while(recv_offset < recv_size2){
		int size = recv(sid, recv_msg2, recv_size2, MSG_WAITALL);
		recv_offset += size;
	}
	cout << "msg size " << recv_size2 << endl;
	if(recv_type2 == EXECUTE_SUCCESS){
		char name[20];
		int argnum;
		memcpy( &argnum, &recv_msg2[20], sizeof(int));
		int argTypes[argnum];
		myUnmarshall(recv_type2, recv_msg2,recv_size2, name, argTypes, args, NULL, NULL, &reasoncode);
		//cout << "answer " << (char *)args[0] << endl;
	}
	else if(recv_type2 == EXECUTE_FAILURE){
		cout << "in failure" << endl;
		myUnmarshall(recv_type2, recv_msg2,recv_size2, NULL, NULL, NULL, NULL, NULL, &reasoncode);
		cout << "reasoncode: " << reasoncode << endl;
		return 0;
	}
    close(sid);
	return 1;
}

int rpcRegister(char* name, int* argTypes, skeleton f){
	int buffersize;
	int type = REGISTER;
	
	// convert char ip to in_addr_t
	struct hostent *host = gethostbyname(serverip);
    struct in_addr **hostip = (struct in_addr**)host->h_addr_list;
	in_addr_t myip = inet_addr(inet_ntoa(**hostip));
	
	char *buffer = myMarshall(type, &buffersize, name, argTypes,
								NULL,myip,serverport,0);
	char myname[20];
	in_addr_t ip = 0;
	int port;
	int arg_num = (buffersize-4-4-20)/sizeof(int);
	int myargTypes[arg_num];	
	myUnmarshall(type, buffer, buffersize, myname, myargTypes, 
				NULL, &ip, &port, 0);
	cout << "ip: " << ip << endl;
	cout << "myip: " << ip << endl;
	cout << "port: " << port << endl;
	cout << "serverport: " << serverport << endl;
	cout << "name: " << myname << endl;
	cout << "argnum: " << arg_num << endl;
	myfunction *myfcn = new myfunction();
	strcpy(myfcn->name,myname);
	myfcn->argnum = arg_num;
	myfcn->argtype = argTypes;
	myfcn->func = f;
	functionlist.push_back(myfcn);

	send(forbinder, &buffersize, sizeof(int),0);
	send(forbinder, &type, sizeof(int),0);
	send(forbinder, buffer, buffersize,0);
	return 0;
}


int rpcExecute(){
	
	//use pthread....
	//first one.. like the server

	//second one wait for the binder
	 /*create two threads*/

    pthread_t threads[2];
    
	
    pthread_create(&threads[0], NULL, provideservice,(void *)0);


    pthread_create(&threads[1], NULL, keepconnection, (void *)1);
    

    pthread_join(threads[1],NULL);
    pthread_kill(threads[0], SIGTSTP);
    /*join two threads*/
    pthread_join(threads[0],NULL);
    
                  
//	provideservice();
    
    return 0;
}

int rpcTerminate(){
	///terminate all server...
	int buffersize = 0;
	int type = TERMINATE;
    int sid = connecttobinder();
	send(sid, &buffersize, sizeof(int),0);
	send(sid, &type, sizeof(int),0);

	return 0;

}